#pragma once
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <set>

class Token
{
protected:
	int tagID;
	std::string lexme;
public:
	Token();
	Token(int tagID, std::string lexme);
	int getTagID();
	std::string getLexme();
	virtual std::string toString();
	virtual std::string toString() const;
};

typedef Token* (*token_handler)(std::string lexme);

class KeywordToken : public Token
{
public:
	KeywordToken(std::string lexme, int type);
	static const int TAGID = 1414;
	static enum KEYWORD
	{
		LET=1, IF, THEN, ELSE,DO, END, AND, OR, WHEN, FUNC, RETURN, BREAK, CONTINUE, BOOL=0x20,I8, I32, FLOAT, DOUBLE, STRING
	};
	int type = 0;
};

class IDToken : public Token
{
public:
	static const int TAGID = 1415;
	IDToken(std::string lexme);
	std::string toString() override;
};

class NumToken : public Token
{
public:
	static const int TAGID = 1416;
	NumToken(std::string lexme);
	std::string toString() override;
};

class RealNumToken : public Token
{
public:
	static const int TAGID = 1417;
	RealNumToken(std::string lexme);
	std::string toString() override;
};

class OpToken : public Token
{

public:
	static enum class Type
	{
		NOP = 0,
		LT = 60,
		GT = 61,
		SHIFT_RIGHT = 1000,
		SHIFT_LEFT,
		LEQ,
		GEQ,
		EQ,
		NEQ,
		LOGIC_OR,
		LOGIC_AND,
		ADD_EQ,
		SUB_EQ,
	};
	static const int TAGID = 1417;
	Type getOpCode();
	OpToken(Type opcode, std::string lexme);
	std::string toString() override;
public:
	Type opcode;
};

class StringToken : public Token
{
public:
	static const int TAGID = 1418;
	StringToken(std::string lexme);
	std::string toString() override;
};

class CharToken : public Token
{
public:
	static const int TAGID = 1419;
	CharToken(std::string lexme);
	std::string toString() override;
};

class Scanner
{
private:
	int lexStart = 0;
	int forward = -1;
	int currStat = 0;
	std::string src;
	static std::vector<std::map<char, int>> trans_table;
	static std::map<int, int> finalStat;
	std::vector<int> stack;
public:
	Scanner(std::string src);
	Token* _nextToken();
	Token* nextToken();
	static std::map<std::string, int> keywords;

};